#include "../Base/StdAfx.h"
#include "Mesh.h"
#include "../Base/DXDevice.h"
#include "../Base/DirectInput.h"
#include "../Base/Camera.h"
#include "../Effects/EffectShadow.h"
#include "../Effects/Effect.h"
#include <Boost/foreach.hpp>
#include "../Base/StringUtil.h"
#include "./OBJ/OBJMesh.h"
#include "./SkyBox/SkyBox.h"
#include "./Terrain/TerrainFlat.h"
#include "./other/Box.h"

bool sortFrontBack(TreeNode* a, TreeNode* b) { if(a->GetDistance() < b->GetDistance()) return true; else return false; }

Mesh::Mesh()
{

	m_Saved = false;
	m_ShadowEffect = new EffectShadow(_T("./Shaders/ShadowMap.fx"));
	m_Effect = new Effect(_T("./Shaders/PNTTShader.fx"));
	m_SkyBox = -1;

	const D3D10_INPUT_ELEMENT_DESC layoutScene[] =
	{
		{ "POSITION",  0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL",    0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 32, D3D10_INPUT_PER_VERTEX_DATA, 0},
	};

	UINT numElementsScene = sizeof(layoutScene)/sizeof(layoutScene[0]);
	D3D10_PASS_DESC passDesc;
	m_ShadowEffect->GetTech()->GetPassByIndex(0)->GetDesc(&passDesc);
	GETDEVICE->CreateInputLayout(layoutScene, numElementsScene, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize,  &m_VertexLayout);

	m_ShadowMap.Init(2048, 2048, false, DXGI_FORMAT_UNKNOWN);

	D3DXMatrixIdentity(&m_LightWorld);
	D3DXMatrixIdentity(&m_LightView);
	D3DXMatrixIdentity(&m_LightProj);

	D3DXMatrixOrthoLH(&m_LightProj, 100, 100, 0.1f, 500.0f);
}

Mesh::~Mesh()
{
	delete m_ShadowEffect;
	delete m_Effect;
	m_VertexLayout->Release();
	BOOST_FOREACH(TreeNode* mesh, m_Meshes) delete mesh;
}

mesh_id Mesh::CreateMesh(const tstring& file, MeshDesc::PhysxState state, const D3DXVECTOR3& pos, const D3DXVECTOR3& rot)
{
	tstring ext;
	mesh_id id = m_Meshes.size();
	StringUtil::GetExtension(file, ext);
	if(ext == _T("obj"))
	{
		MeshDesc mDesc;
		mDesc.castShadow = true;
		mDesc.inLib = false;
		mDesc.state = state;
		mDesc.name = file;
		mDesc.id = id;
		mDesc.animated = true;
		m_Meshes.push_back(new OBJMesh(mDesc, pos, rot));
	}else if(ext == _T("dae")) {

	}
	return id;
}

void Mesh::Tick(float dTime)
{
	D3DXVECTOR3 posCam = DXDEVICE->GetCamera()->GetPos();
	BOOST_FOREACH(TreeNode* mesh, m_Meshes) mesh->GetTotalTranspose();
	BOOST_FOREACH(TreeNode* mesh, m_Meshes)
	{
		if(mesh->GetDesc().name == _T("SKYBOX")) 
			mesh->SetDistance(999999999.0f);
		else
		{
			D3DXVECTOR3 posMesh = mesh->GetWorldMatrix().m[3];
			D3DXVECTOR3 dist = posCam - posMesh;
			mesh->SetDistance(D3DXVec3Length(&dist));
		}
	}
	sort(m_Meshes.begin(), m_Meshes.end(), sortFrontBack);
	if(m_SkyBox != -1)
		m_Meshes.at(m_Meshes.size() -1)->Tick(dTime);
}

int Mesh::GetCount() const
{
	return (int)m_Meshes.size();
}

void Mesh::Render()
{
	GETDEVICE->IASetInputLayout(m_VertexLayout);

	D3DXMATRIX lView, lProj;
	DXDEVICE->GetMatrixen(lView, lProj);
	Light light = DXDEVICE->GetLight();

	// Generate Shadow
	m_ShadowMap.begin();	
	D3DXMatrixLookAtLH(&m_LightView, &light.pos, &D3DXVECTOR3(0,0,0), &D3DXVECTOR3(0,1,0));
	D3D10_TECHNIQUE_DESC tech;
	m_ShadowEffect->GetTech()->GetDesc( &tech );
	for(UINT p = 0; p < tech.Passes; ++p)
	{
		ID3D10EffectPass* pass = m_ShadowEffect->GetTech()->GetPassByIndex(p);
		BOOST_FOREACH(TreeNode* mesh, m_Meshes)
		{	
			if(mesh->CastShadow())
			{
				m_ShadowEffect->SetMatrixen(mesh->GetWorldMatrix(), m_LightView, m_LightProj);
				pass->Apply(0);
				if(!mesh->GetDesc().ownRender && !mesh->GetDesc().animated)
					mesh->Draw();
			}
		}
	}
	m_ShadowMap.end();


	if(!m_Saved)// && DirectInput::GetSingleton()->KeyDown(DIK_U))
	{
		m_Saved = true;
		m_ShadowMap.SaveDepth(_T("./DepthSave.dds"));
	}

	// Draw meshes
	m_Effect->GetTech()->GetDesc( &tech );
	
	m_Effect->SetEyePos(DXDEVICE->GetCamera()->GetPos());
	m_Effect->SetLight(light.pos, light.diffuse, light.spec, light.ambient);
	for(UINT p = 0; p < tech.Passes; ++p)
	{
		ID3D10EffectPass* pass = m_Effect->GetTech()->GetPassByIndex(p);
		BOOST_FOREACH(TreeNode* mesh, m_Meshes)
		{
			if(mesh->CastShadow())
			{
				if(mesh->GetName() == _T("FLATTERRAIN"))
					m_Effect->SetTexScale(70, 70);
				else
					m_Effect->SetTexScale(1, 1);
				m_Effect->SetMatrixen(mesh->GetWorldMatrix(), lView, lProj);
				m_Effect->SetLightWVP(mesh->GetWorldMatrix() * m_LightView * m_LightProj);
				m_Effect->SetShadowMap(m_ShadowMap.depthMap());
				m_Effect->SetTexture(mesh->GetTexture());
				pass->Apply(0);
				if(!mesh->GetDesc().ownRender && !mesh->GetDesc().animated)
					mesh->Draw();
			}
		}
	}

	// Draw the rest
	//BOOST_FOREACH(TreeNode* mesh, m_Meshes)
	//{
	//	if(mesh->GetDesc().animated)
	//		mesh->Draw();
	//}
	if(m_SkyBox != -1)
	{
		m_Meshes.at(m_Meshes.size() - 1)->Draw(); // Skybox wordt altijd als laatste gesorteerd.
	}

	// Reset shader so the depthmap can be used again.
	m_Effect->ResetShader();
	m_Effect->GetTech()->GetPassByIndex(0)->Apply(0);
}

void Mesh::AddForce( const D3DXVECTOR3& force )
{
	BOOST_FOREACH(TreeNode* mesh, m_Meshes) mesh->AddForce(force);
}

mesh_id Mesh::CreateSkybox( const tstring& file, int size )
{
	if(m_SkyBox == -1)
	{
		m_SkyBox = m_Meshes.size();
		SkyBox* temp = new SkyBox(file, (float)size);
		temp->GetDesc().id = m_SkyBox;
		temp->GetDesc().name = _T("SKYBOX");
		m_Meshes.push_back(temp);
	}
	return m_SkyBox;
}

mesh_id Mesh::CreateFlatTerrain( const tstring& file, int size )
{
	int num = m_Meshes.size();
	TerrainFlat* temp = new TerrainFlat(D3DXVECTOR3(0, 0, 0), (float)size, (float)size, file);
	temp->GetDesc().id = num;
	temp->GetDesc().name = _T("FLATTERRAIN");
	temp->GetDesc().castShadow = true;
	m_Meshes.push_back(temp);
	return num;
}

mesh_id Mesh::CreateTerrain( const tstring& file, int size )
{
	return 0;
}

TreeNode* Mesh::GetMesh( mesh_id meshId )
{
	if(meshId >= m_Meshes.size())
	{
		LOG(_T("ERROR: Te hoog mesh_id."));
		return NULL;
	}else
		for(size_t t = 0; t < m_Meshes.size(); ++t)
		{
			if(m_Meshes.at(t)->GetDesc().id == meshId) 
				return m_Meshes.at(t);
		}
	return NULL;
}

mesh_id Mesh::CreateBox( const tstring& name, const tstring& file, const tstring& normalMap, const tstring& specMap, float width, float length, float height, const D3DXVECTOR3& pos , MeshDesc::PhysxState state)
{
	mesh_id id =  m_Meshes.size();
	MeshDesc mDesc;
	mDesc.castShadow = true;
	mDesc.state = state;
	mDesc.diffuseMap = file;
	mDesc.normalMap = normalMap;
	mDesc.specMap = specMap;
	Box* temp = new Box(mDesc, pos, width, length, height);
	temp->GetDesc().id = id;
	temp->GetDesc().name = name;
	m_Meshes.push_back(temp);
	return id;
}

mesh_id Mesh::GetId( const tstring& name )
{
	BOOST_FOREACH(TreeNode* node, m_Meshes)
	{
		if(node->GetName() == name)
			return node->GetDesc().id;
	}
	return -1;
}